package com.xrui.hbase.tools;

import com.google.common.base.Preconditions;
import com.moz.kiji.common.flags.Flag;

import com.xrui.hbase.HBase;
import com.xrui.hbase.HBaseURI;
import com.xrui.hbase.schema.TableLayout;
import com.xrui.hbase.util.ResourceUtils;
import com.xrui.hbase.util.ToJson;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

/**
 * Command-line tool for reading table layouts.
 * <p>
 * <h2>Examples:</h2>
 * Write the layout of a table to a file:
 * <pre>
 *   hbase layout --table=hbase://my-hbase/my-instance/my-table/ \
 *       --do=dump --max-version=3 --write-to=my-output-file
 * </pre>
 * Prints layout for a table:
 * <pre>
 *   hbase layout --table=hbase://my-hbase/my-instance/my-table/ \
 *       --do=set --layout=my-input-file
 * </pre>
 */
public final class LayoutTool extends BaseTool {
    @Flag(name = "table", usage = "The HBaseURI of the table to use.")
    private String mTableURIFlag = null;

    @Flag(name = "layout",
        usage = "Path to the file containing the layout update, in JSON.")
    private String mLayout = "";

    @Flag(name = "write-to",
        usage = "Write layout(s) to this file(s). "
            + "Empty means write to console output. "
            + "A '.json' extension is appended to this parameter.")
    private String mWriteTo = "";

    /**
     * URI of the table to edit or print the layout of.
     */
    private HBaseURI mTableURI;

    /**
     * The HBase to use to set layouts.
     */
    private HBase mHBase;

    /**
     * Determines whether a path has its filesystem explicitly specified.  Did it start
     * with "hdfs://" or "file://"?
     *
     * @param path The path to check.
     * @return Whether a file system was explicitly specified in the path.
     */
    protected static boolean fileSystemSpecified(Path path) {
        return null != path.toUri().getScheme();
    }

    /**
     * Program entry point.
     *
     * @param args The command-line arguments.
     * @throws Exception If there is an error.
     */
    public static void main(String[] args) throws Exception {
        System.exit(new HBaseToolLauncher().run(new LayoutTool(), args));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getName() {
        return "layout";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getDescription() {
        return "View or modify hbase table layouts.";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getCategory() {
        return "Schema";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void validateFlags() throws Exception {
        Preconditions.checkArgument((mTableURIFlag != null) && !mTableURIFlag.isEmpty(),
            "Specify a table with --table=hbase://hbase-address/hbase-instance/table");
        mTableURI = HBaseURI.newBuilder(mTableURIFlag).build();
        Preconditions.checkArgument(mTableURI.getTable() != null,
            "Specify a table with --table=hbase://hbase-address/hbase-instance/table");
    }

    /**
     * Implements the --do=dump operation.
     *
     * @throws Exception on error.
     */
    private void dumpLayout() throws Exception {
        final TableLayout layout = mHBase.getTableLayout(mTableURI);
        String json = ToJson.toJsonString(layout);
        if (mWriteTo.isEmpty()) {
            System.out.println(json);
        } else {
            final String fileName = String.format("%s.json", mWriteTo);
            final FileOutputStream fos = new FileOutputStream(fileName);
            try {
                fos.write(Bytes.toBytes(json));
            } finally {
                ResourceUtils.closeOrLog(fos);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void setup() throws Exception {
        super.setup();
        mHBase = HBase.open(mTableURI, getConf());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void cleanup() throws IOException {
        ResourceUtils.releaseOrLog(mHBase);
        mHBase = null;
        super.cleanup();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected int run(List<String> nonFlagArgs) throws Exception {
        dumpLayout();
        return SUCCESS;
    }
}
